﻿using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Text;
using System.Threading;
using System.Threading.Tasks;

namespace ConsoleApp2
{
  internal class Program
  {
    static Random random = new Random();
    static int[] delay = new int[] { 300, 400, 500, 600, 700, 800, 900, 222, 333, 2050, 3000 };
    static void LongRunningFoo(bool is2=false)
    {
      string txt = "";
      if (is2) {
        txt = "---------------------";
      }
      Console.WriteLine($"{txt}Task Start");
      var r = random.Next(0, 11);
      Console.WriteLine($"{txt}预计用时：{delay[r]}");
      Thread.Sleep(delay[r]);
      Console.WriteLine($"{txt}Task down");
    }
    static void Main(string[] args)
    {

      Task.Factory.StartNew(async () =>
      {
        var start = Stopwatch.StartNew();

        while (true)
        {
          Thread.Sleep(500);
          CancelableThreadTask threadTask = new CancelableThreadTask(()=>LongRunningFoo(true));
          start.Restart();
          //threadTask.Action = LongRunningFoo;
          CancellationTokenSource cts = new CancellationTokenSource(2000);

          try
          {
            await threadTask.RunAsync(cts.Token);
          }
          catch (Exception)
          {
            Console.WriteLine("---------------------task1 is cancel");
          }


          Console.WriteLine($"---------------------task1用时：{start.ElapsedMilliseconds}ms");
          Console.WriteLine();
          cts.Dispose();
        }
      });

      Task.Factory.StartNew(async () =>
      {
        var start = Stopwatch.StartNew();
        Thread.Sleep(300);
        while (true)
        {
          Thread.Sleep(500);
          CancelableThreadTask threadTask = new CancelableThreadTask(() => LongRunningFoo());
          start.Restart();
          //threadTask.Action = LongRunningFoo;
          using (var cts = new CancellationTokenSource(2000))

            try
            {
              await threadTask.RunAsync(cts.Token);
            }
            catch (Exception)
            {
              Console.WriteLine("task2 is cancel");
            }


          Console.WriteLine($"task2 用时：{start.ElapsedMilliseconds}ms");
          Console.WriteLine();

        }
      });
      Console.WriteLine("wait");
      Console.ReadLine();
    }
  }

  public class CancelableThreadTask
  {
    private Thread _thread;
    private Action _action;
    private readonly Action<Exception> _onError;
    private readonly Action _onCompleted;
    private TaskCompletionSource<int> _tcs;

    private int _isRunning = 0;

    public Action Action { get => _action; set => _action = value; }

    public CancelableThreadTask(Action action, Action<Exception> onError = null, Action onCompleted = null)
    {
      _action = action ?? throw new ArgumentNullException(nameof(action));
      _onError = onError;
      _onCompleted = onCompleted;
    }

    public Task RunAsync(CancellationToken token)
    {
      if (Interlocked.CompareExchange(ref _isRunning, 1, 0) == 1)
        throw new InvalidOperationException("Task is already running");

      _tcs = new TaskCompletionSource<int>();
      _thread = new Thread(() =>
      {
        try
        {
          _action();
          _tcs.SetResult(0);
          _onCompleted?.Invoke();
        }
        catch (Exception ex)
        {
          if (ex is ThreadInterruptedException)
            _tcs.TrySetCanceled(token);
          else
            _tcs.TrySetException(ex);
          _onError?.Invoke(ex);
        }
        finally
        {
          Interlocked.Exchange(ref _isRunning, 0);
        }
      });

      token.Register(() =>
      {
        if (Interlocked.CompareExchange(ref _isRunning, 0, 1) == 1)
        {
          _thread.Interrupt();
          _thread.Join();
          _tcs.TrySetCanceled(token);
        }
      });

      _thread.Start();

      return _tcs.Task;
    }
  }
}
